/******************************************************************************
* Filename:    Chat Client.cpp
* Auther:	   Cheng Zhuo
*			   XPEC Entertainment Inc., Shanghai.
*
* Description: The server of chat application
*******************************************************************************
* (c) 2010 XPEC Entertainment Inc.,Shanghai. All rights reserved.
******************************************************************************/

#define _CRT_SECURE_NO_WARNINGS

#include "MessageIdentifiers.h"
#include "RakNetworkFactory.h"
#include "RakPeerInterface.h"
#include "RakNetStatistics.h"
#include "RakNetTypes.h"
#include "BitStream.h"
#include "ChatClientPlugin.h"
#include "ChatOrder.h"
#include <assert.h>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#ifdef _WIN32
#include "Kbhit.h"
#include <windows.h> // Sleep
#else
#include "Kbhit.h"
#include <unistd.h> // usleep
#endif

// Declaration of functions
//unsigned char GetPacketIdentifier(Packet *p);
//bool isOrder( char* message, char* order, int pos );
//bool isNum( char* message, int begin, int end );
//char* subString( char* message, int begin, int end );

/******************************************************************************
 * Main function of this application                               
 * 
 * @return 1 on finished
 *****************************************************************************/
int main(void)
{
	RakNetStatistics *rss;
	// Pointers to the interfaces of our server and client.
	// Note we can easily have both in the same program
	RakPeerInterface *client=RakNetworkFactory::GetRakPeerInterface();
    // client->InitializeSecurity(0,0,0,0);
	
	// Holds packets
	Packet* p;

	// GetPacketIdentifier returns this
	unsigned char packetIdentifier;

	// Just so we can remember where the packet came from
	bool isServer;

	// Record the first client that connects to us so we can pass it to the ping function
	// SystemAddress clientID=UNASSIGNED_SYSTEM_ADDRESS;

	// Chat client plugin instance
	ChatApp::ChatClientPlugin chatclient;

	// Crude interface

	// Holds user data
	char ip[30], serverPort[30], clientPort[30];

	// A client
	isServer=false;

	// Output information
	printf("This is a sample implementation of a text based chat client.\n");
	printf("Connect to the project 'Chat Example Server'.\n");
	printf("Difficulty: Beginner\n\n");

	// Get our input
	puts("Enter the client port to listen on");
	gets(clientPort);
	if (clientPort[0]==0)
		strcpy(clientPort, "0");

	puts("Enter IP to connect to");
	gets(ip);
	client->AllowConnectionResponseIPMigration(false);
	if (ip[0]==0)
	strcpy(ip, "127.0.0.1\0");
	
		
	puts("Enter the port to connect to");
	gets(serverPort);
	if (serverPort[0]==0)
	strcpy(serverPort, "20120");

	// Connecting the client is very simple.  0 means we don't care about
	// a connectionValidationInteger, and false for low priority threads
	SocketDescriptor socketDescriptor(atoi(clientPort),0);
	client->Startup(1,30,&socketDescriptor, 1);
	client->SetOccasionalPing(true);
	bool b = client->Connect(ip, 
		                     atoi(serverPort), 
							 "Rumpelstiltskin", 
							 (int) strlen("Rumpelstiltskin"));
	client->AttachPlugin( &chatclient );

	if ( b )
	{
		puts("Attempting connection");
	} // End if
	else
	{
		puts("Bad connection attempt.  Terminating.");
		exit(1);
	} // End else

	printf("My IP is %s\n", client->GetLocalIP(0));
	printf("My GUID is %s\n", client->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS).ToString());
	puts("'quit' to quit. 'stat' to show stats. 'ping' to ping. 'disconnect' to disconnect.\n'friend clientID' to send a friend request. 'to clientID' to talk with friend\nType to talk.");
	
	char message[2048];          // String of input message

	// Loop for input
	while ( 1 )
	{
		// This sleep keeps RakNet responsive
#ifdef _WIN32
		Sleep(30);
#else
		usleep(30 * 1000);
#endif


		if ( kbhit() )
		{
			// Notice what is not here: something to keep our network running.  It's
			// fine to block on gets or anything we want
			// Because the network engine was painstakingly written using threads.
			gets(message);

			// Quit order
			if ( strcmp(message, "quit")==0 )
			{
				puts("Quitting.");
				break;
			} // End if

			// Stat order
			if ( strcmp(message, "stat")==0 )
			{
				rss=client->GetStatistics(client->GetSystemAddressFromIndex(0));
				StatisticsToString(rss, message, 2);
				printf("%s", message);
				printf("Ping=%i\n", client->GetAveragePing(client->GetSystemAddressFromIndex(0)));

				continue;
			} // End if

			// Disconnect order
			if ( strcmp(message, "disconnect")==0 )
			{
				client->CloseConnection(client->GetSystemAddressFromIndex(0),true,0);
				printf("Disconnecting.\n");

				continue;
			} // End if

			// Ping order
			if ( strcmp(message, "ping")==0 )
			{
				if (client->GetSystemAddressFromIndex(0)!=UNASSIGNED_SYSTEM_ADDRESS)
					client->Ping(client->GetSystemAddressFromIndex(0));

				continue;
			} // End if

			chatclient.OnSend( client, message );
		}

		// Get a packet from either the server or the client
		p = client->Receive();
		if ( p==0 )
		{
			continue; // Didn't get any packets
		} // End if

		// We got a packet, get the identifier with our handy function
		packetIdentifier = chatclient.GetPacketId(p);

		// Check if this is a network message packet
		switch ( packetIdentifier )
		{
			case ID_DISCONNECTION_NOTIFICATION:
				{
				    // Connection lost normally
				    printf("ID_DISCONNECTION_NOTIFICATION\n");
				} // End case
				break;

			case ID_ALREADY_CONNECTED:
				{
					// Connection lost normally
					printf("ID_ALREADY_CONNECTED\n");
				} // End case				
				break;

			// Server telling the clients of another client disconnecting gracefully.  
		    // You can manually broadcast this in a peer to peer environment if you want.
			case ID_REMOTE_DISCONNECTION_NOTIFICATION: 
				{
					printf("ID_REMOTE_DISCONNECTION_NOTIFICATION\n");
				} // End case				
				break;

			// Server telling the clients of another client disconnecting forcefully.
			//You can manually broadcast this in a peer to peer enviroment if you want.
			case ID_REMOTE_CONNECTION_LOST:   
				{
					printf("ID_REMOTE_CONNECTION_LOST\n");
				} // End case				
				break;

            // Server telling the clients of another client connecting.
			// You can manually broadcast this in a peer to peer enviroment if you want.
			case ID_REMOTE_NEW_INCOMING_CONNECTION:   
				{
					printf("ID_REMOTE_NEW_INCOMING_CONNECTION\n");
				} // End case				
				break;

            // Banned from this server
			case ID_CONNECTION_BANNED: 
				{
					printf("We are banned from this server.\n");
				} // End case				
				break;			

			case ID_CONNECTION_ATTEMPT_FAILED:
				{
					printf("Connection attempt failed\n");
				} // End case				
				break;

			case ID_NO_FREE_INCOMING_CONNECTIONS:
				{
					// Sorry, the server is full.  I don't do anything here but
					// A real app should tell the user
					printf("ID_NO_FREE_INCOMING_CONNECTIONS\n");
				} // End case				
				break;

			case ID_MODIFIED_PACKET:
				{
					// Cheater!
					printf("ID_MODIFIED_PACKET\n");
				} // End case				
				break;

			case ID_INVALID_PASSWORD:
				{
					printf("ID_INVALID_PASSWORD\n");
				} // End case				
				break;

			case ID_CONNECTION_LOST:
				{
					// Couldn't deliver a reliable packet
					// terminated
					printf("ID_CONNECTION_LOST\n");
				} // End case
				break;

			case ID_CONNECTION_REQUEST_ACCEPTED:
				{
					// This tells the client they have connected
					printf("ID_CONNECTION_REQUEST_ACCEPTED to %s with GUID %s\n", 
						   p->systemAddress.ToString(), 
						   p->guid.ToString());
				} // End case
				break;

			default:
				// It's a client, so just show the message
				printf("%s\n", p->data);

                sprintf_s(message, 2048, "%s\n", p->data);

                char filename[64];
                sprintf_s(filename, 64, "chat_record_%s.txt", clientPort);

                FILE *pFile = NULL;

                // Open file
                if ( fopen_s( &pFile, filename, "a" ) != 0 )
                {
                    return false;
                } // End if

                fwrite( message, sizeof(char), strlen(message) + 1, pFile );

                // Close file
                fclose(pFile);

				break;
		} // End switch


		// We're done with the packet
		client->DeallocatePacket(p);
	}

	// Be nice and let the server know we quit.
	client->Shutdown(300);

	// We're done with the network
	RakNetworkFactory::DestroyRakPeerInterface(client);

	return 0;
} // End of main

///******************************************************************************
//* Function for getting the identifier of packet     
//* If the first byte is ID_TIMESTAMP, then we want the 5th byte
//* Otherwise we want the 1st byte
//*
//* @param p pointer point to a packet
//* 
//* @return The unsigned char signed the identifier
//*****************************************************************************/
//unsigned char GetPacketIdentifier( Packet *p )
//{
//	if ( p == 0 )
//	{
//		return 255;
//	} // End if
//
//	if ( (unsigned char)p->data[0] == ID_TIMESTAMP )
//	{
//		assert(p->length > sizeof(unsigned char) + sizeof(unsigned long));
//		return (unsigned char) p->data[sizeof(unsigned char) + sizeof(unsigned long)];
//	} // End if
//	else
//	{
//		return (unsigned char) p->data[0];
//	} // End else
//} // End of GetPacketIdentifier
//
///******************************************************************************
// * Function for checking the order message     
// * Check a message is or not a order by compare the string
// *
// * @param message message which will be check
// * @param order   string of the order
// * @param pos     position in the message where to check from
// * 
// * @return True while it's an order, false while not
// *****************************************************************************/
//bool isOrder( char* message, char* order, int pos )
//{
//	if ( ( pos > strlen(message) - strlen(order) - 1 ) || pos < 0)
//	{
//		return false;
//	} // End if
//
//	for ( int i = 0; i < strlen(order); ++i )
//	{
//		if ( message[pos + i] != order[i] )
//		{
//			return false;
//		} // End if
//	} // End for
//
//	return true;
//} // End of isOrder
//
///******************************************************************************
//* Function for checking the number message     
//* Check a string is or not a number
//* 
//* @param message message which will be check
//* @param begin   position in the message where to check begin from
//* @param end     position in the message where to be end
//* 
//* @return True while it's an number, false while not
//*****************************************************************************/
//bool isNum( char* message, int begin, int end )
//{
//	if ( end > strlen(message) || begin >= end || begin < 0 || end < 0 )\
//	{
//		return false;
//	} // End if
//
//	// Check if every character is a number
//	for ( int i = begin; i < end; ++i )
//	{
//		if ( message[i] > '9' || message[i] < '0')
//		{
//			return false;
//		} // End if
//	} // End for
//
//	return true;
//} // End of isNum
//
///******************************************************************************
//* Function for cutting off a shorter string from a whole message 
//* Get a shorter sequential string in a message
//* 
//* @param message message which will be intercepted
//* @param begin   position in the message where to intercept from
//* @param end     position in the message where to be end
//* 
//* @return The pointer of sub-string
//*****************************************************************************/
//char* subString( char* message, int begin, int end )
//{
//	char* temp = (char *)malloc(end - begin + 1);
//
//	if ( end > strlen(message) || begin >= end || begin < 0 || end < 0 )
//	{
//		temp[0] = '\0';
//		return temp;
//	} // End if
//
//	for ( int i = 0; i < end - begin; ++i )
//	{
//		temp[i] = message[begin + i];
//	} // End for
//	temp[end - begin] = '\0';
//
//	return temp;
//} // End of subString

